// Copyright (c) 2019 Presto Labs Pte. Ltd.
// Author: jaewon

#include "coin1/base/exception_util.h"

#include <utility>

#include <backward.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <glog/logging.h>

namespace detail {

std::string Stacktrace(bool strip_after_caller) noexcept {
  static const char kFunctionName[] = "detail::Stacktrace";

  backward::StackTrace st;
  st.load_here(128);
  backward::Printer p;
  p.object = false;
  p.address = true;
  p.snippet = false;
  p.color_mode = backward::ColorMode::automatic;

  // Strips stack up to this function.
  if (strip_after_caller) {
    backward::TraceResolver resolver;
    resolver.load_stacktrace(st);

    bool skip_depth_found = false;
    int skip_depth = 1;
    for (; skip_depth <= 5 && skip_depth <= st.size(); ++skip_depth) {
      auto trace = resolver.resolve(st[skip_depth - 1]);
      if (boost::algorithm::starts_with(trace.object_function, kFunctionName)) {
        skip_depth_found = true;
        break;
      }
    }

    if (skip_depth_found) {
      st.skip_n_firsts(skip_depth);
    }
  }

  std::stringstream ss;
  p.print(st, ss);
  return ss.str();
}

ExceptionMessage::ExceptionMessage(const char* file, int line, std::string&& stacktrace_msg)
    : stacktrace_msg_(std::move(stacktrace_msg)) {
  if (file) {
    stream_ << "[" << file << ":" << line << "] ";
  }
}

std::string ExceptionMessage::str() {
  if (!stacktrace_msg_.empty()) {
    stream_ << std::endl << std::endl << stacktrace_msg_;
  }
  return stream_.str();
}

}  // namespace detail
